package com.nycreativity.elecreativity.service.impl;

import cn.hutool.extra.pinyin.PinyinUtil
import com.baomidou.mybatisplus.core.metadata.IPage
import com.baomidou.mybatisplus.core.toolkit.IdWorker
import com.baomidou.mybatisplus.extension.kotlin.KtQueryWrapper
import com.baomidou.mybatisplus.extension.kotlin.KtUpdateWrapper
import com.baomidou.mybatisplus.extension.plugins.pagination.Page
import com.nycreativity.elecreativity.entity.po.User;
import com.nycreativity.elecreativity.mapper.UserMapper;
import com.nycreativity.elecreativity.service.UserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nycreativity.elecreativity.entity.dto.UserRoleDTO
import com.nycreativity.elecreativity.entity.enums.IsDefaultEnum
import com.nycreativity.elecreativity.entity.enums.IsDelEnum
import com.nycreativity.elecreativity.entity.enums.RoleEnum
import com.nycreativity.elecreativity.entity.enums.UserStatusEnum
import com.nycreativity.elecreativity.entity.param.PageParam
import com.nycreativity.elecreativity.entity.param.user.*
import com.nycreativity.elecreativity.entity.po.Address
import com.nycreativity.elecreativity.entity.po.UserRole
import com.nycreativity.elecreativity.entity.vo.user.UserVO
import com.nycreativity.elecreativity.mapper.AddressMapper
import com.nycreativity.elecreativity.mapper.vomapper.UserVOMapper
import com.nycreativity.elecreativity.service.UserRoleService
import com.nycreativity.elecreativity.util.MD5Util
import com.nycreativity.elecreativity.util.MappingUtil
import com.nycreativity.elecreativity.util.usersession.UserCache
import com.nycreativity.elecreativity.util.usersession.LoginUser
import com.nycreativity.elecreativity.util.usersession.UserContext
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional
import org.springframework.util.ObjectUtils
import java.time.LocalDateTime
import javax.annotation.Resource

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author sxt && ttxxi
 * @since 2021-03-11
 */
@Service
class UserServiceImpl : ServiceImpl<UserMapper, User>(), UserService {

    @Resource
    lateinit var userMapper: UserMapper
    @Resource
    lateinit var userVOMapper: UserVOMapper

    @Resource
    lateinit var addressMapper: AddressMapper
    @Resource
    lateinit var userRoleService: UserRoleService

    @Transactional
    override fun addUser(userAddParam: UserAddParam): Boolean {

        //判断号码是否可用
        if(loginViaPhoneNum(userAddParam.phoneNum!!) != null){
            return false
        }

        //添加用户
        val userId = IdWorker.getId()
        val user = MappingUtil.getMappingObject(userAddParam, User::class.java)
        user.apply {
            id = userId
            alias = getAlias(name!!)
            password = MD5Util.MD5(password)
            createTime = LocalDateTime.now()
            status = UserStatusEnum.ACTIVE.status
        }
        userMapper.insert(user)

        //添加地址
        val address = MappingUtil.getMappingObject(userAddParam, Address::class.java)
        address.apply {
            this.userId = userId
            //设为默认
            isDefault = IsDefaultEnum.YES.code
            createTime = LocalDateTime.now()
            creator = userId
        }
        addressMapper.insert(address)

        //添加用户角色信息,此接口默认都添加用户角色
        val userRole = UserRole()
        userRole.userId = userId
        userRole.roleId = RoleEnum.USER.roleId
        userRoleService.save(userRole)

        return true
    }

    override fun loginViaPassword(phoneNum: String, password: String): UserVO? {
        val queryWrapper = KtQueryWrapper(User::class.java)
            .eq(User::phoneNum, phoneNum)
            .eq(User::password, MD5Util.MD5(password))
            .eq(Address::isDel, IsDelEnum.NO.code)
        //登录失败直接返回null
        val user = userMapper.selectOne(queryWrapper) ?: return null

        //登录成功，先存入缓存，再返回token
        val token = saveLoginUserCache(user, userRoleService.queryUserRoleDTOByUserId(user.id!!))
        val userVO = MappingUtil.getMappingObject(user, UserVO::class.java)
        userVO.token = token.toString()
        return userVO
    }

    override fun loginViaPhoneNum(phoneNum: String): UserVO? {
        val queryWrapper = KtQueryWrapper(User::class.java)
            .eq(User::phoneNum, phoneNum);
        val list = userMapper.selectList(queryWrapper)
        if (list == null || list.isEmpty()){
            return null;
        }
        if (list.size > 1){
            log.error("号码为${phoneNum}的账户不只一个")
            throw RuntimeException("号码为${phoneNum}的账户不只一个")
        }
        val user = list[0]
        //登录成功，先存入缓存，再返回token
        val token = saveLoginUserCache(user, userRoleService.queryUserRoleDTOByUserId(user.id!!))
        val userVO = MappingUtil.getMappingObject(user, UserVO::class.java)
        userVO.token = token.toString()
        return userVO
    }

    override fun loginViaEmail(email: String, password: String): UserVO? {
        val queryWrapper = KtQueryWrapper(User::class.java)
            .eq(User::email, email)
            .eq(User::password, MD5Util.MD5(password))
            .eq(Address::isDel, IsDelEnum.NO.code)
        //登录失败直接返回null
        val user = userMapper.selectOne(queryWrapper) ?: return null

        //登录成功，先存入缓存，再返回token
        val token = saveLoginUserCache(user, userRoleService.queryUserRoleDTOByUserId(user.id!!))
        val userVO = MappingUtil.getMappingObject(user, UserVO::class.java)
        userVO.token = token.toString()
        return userVO
    }

    override fun updatePwdByPhoneNum(phoneNum: String, password: String): Boolean {
        val queryWrapper = KtQueryWrapper(User::class.java)
            .eq(User::phoneNum, phoneNum)
            .eq(Address::isDel, IsDelEnum.NO.code)
        //查询失败直接返回false
        val user = userMapper.selectOne(queryWrapper) ?: return false
        user.password = MD5Util.MD5(password)
        user.modifyTime = LocalDateTime.now()
        userMapper.updateById(user)
        return true
    }

    override fun updateById(userUpdateParam: UserUpdateParam) {
        val user = MappingUtil.getMappingObject(userUpdateParam, User::class.java)
        if (!ObjectUtils.isEmpty(user.name)){
            user.alias = getAlias(user.name!!)
        }
        user.modifyTime = LocalDateTime.now()
        userMapper.updateById(user)
    }

    override fun updatePhoneNum(userUpdatePhoneNumParam: UserUpdatePhoneNumParam): Boolean {
        val user = userMapper.selectById(userUpdatePhoneNumParam.id) ?: return false
        user.modifyTime = LocalDateTime.now()
        userMapper.updateById(user)
        return true
    }

    override fun updatePwd(userUpdatePwdParam: UserUpdatePwdParam): Boolean {
        val user = userMapper.selectById(userUpdatePwdParam.id)?: return false
        //旧密码错误
        if (!user.password.equals(MD5Util.MD5(userUpdatePwdParam.oldPwd))){
            return false
        }
        user.password = MD5Util.MD5(userUpdatePwdParam.newPwd)
        user.modifyTime = LocalDateTime.now()
        userMapper.updateById(user)
        return true
    }

    override fun queryByPage(pageParam: PageParam): IPage<UserVO> {
        val page = Page<UserVO>(pageParam.currentPage.toLong(), pageParam.pageSize.toLong())
        return userVOMapper.selectPage(page, null)
    }

    override fun updateStatus(userUpdateStatusParam: UserUpdateStatusParam): Boolean {
        if (UserStatusEnum.getTypeByValue(userUpdateStatusParam.status!!) == null){
            return false
        }
        val user = MappingUtil.getMappingObject(userUpdateStatusParam, User::class.java)
        user.modifier = UserContext.get().id
        user.modifyTime = LocalDateTime.now()
        userMapper.updateById(user)
        return true
    }

    /**
     * 保存user到cache，即保存了用户登录的状态
     */
    private fun saveLoginUserCache(user: User, userRoleDTOList : List<UserRoleDTO>): Long {
        val loginUser = LoginUser(user.id, user.name, userRoleDTOList)
        //存入CacheManager！！！
        //token为返回给前端的凭证，之后每一次请求都需要在request header中加上该token
        val token = IdWorker.getId()
        UserCache.put(token, loginUser)
        return token
    }

    private fun getAlias(name: String): String{
        //第一个字符取英文大写
        var firstChar = name[0]
        if (PinyinUtil.isChinese(firstChar)){
            firstChar = PinyinUtil.getFirstLetter(firstChar)
        }
        return firstChar.toUpperCase().toString()
    }


}

